#include "RedLua.hpp"

namespace red {
	Lua::Lua() : mState(lua_open()){
		luaL_openlibs(mState);
	}

	Lua::~Lua(){
		if(mState){
			lua_close(mState);
			mState = 0;
		}
	}

	bool Lua::LoadScript( const std::string &pName ){
		int script = luaL_dofile(mState, pName.c_str());

		// Check errors if one arise
		return !CheckError(script);
	}

	void Lua::RegisterFunction(const std::string &pName, LuaFunctionType pFunction){
		lua_register(mState, pName.c_str(), pFunction);
	}

	bool Lua::CheckError(int pStatus){
		if(pStatus != 0){
			if(pStatus == LUA_ERRFILE)
				std::cout << "-- Lua File Error : " << lua_tostring(mState, -1) << std::endl;
			else if(pStatus == LUA_ERRSYNTAX)
				std::cout << "-- Lua Syntax Error : " << lua_tostring(mState, -1) << std::endl;
			else
				std::cout << "-- Lua Error : " << lua_tostring(mState, -1) << std::endl;
			lua_pop(mState, 1);
		}

		return pStatus != 0;
	}

	s32 Lua::GetArgCount(lua_State* pState){
		return lua_gettop(pState);
	}

	LuaValue Lua::GetGlobalVar( const std::string &pVariable ){
		lua_getglobal(mState, pVariable.c_str());

		// Find the type
		if(lua_isboolean(mState, -1))
			return LuaValue(lua_toboolean(mState, -1));
		else if(lua_isnumber(mState, -1))
			return LuaValue(lua_tonumber(mState, -1));
		else if(lua_isstring(mState, -1))
			return LuaValue(lua_tostring(mState, -1));
		
		return LuaValue();
	}

	LuaValue Lua::CallFunction( const std::string &pFuncName, const LuaValueTable &pParams){
		// Get the parameter count
		int argCount = pParams.size();

		// Retrieve function in lua code
		lua_getglobal(mState, pFuncName.c_str());

		// If parameters are given, send them one by one
		if(argCount){
			std::string paramType;
			for(u32 i = 0; i < pParams.size(); ++i){
				paramType = pParams[i].GetTypeName();
				if(paramType == "bool")
					lua_pushboolean(mState, pParams[i].ToBool());
				else if(paramType == "number")
					lua_pushnumber(mState, pParams[i].ToFloat());
				else if(paramType == "string")
					lua_pushstring(mState, pParams[i].ToString().c_str());
				else
					--argCount;
			}
		}

		// Function call with our parameters
		int error = lua_pcall(mState, argCount, LUA_MULTRET, 0);
		if(CheckError(error))
			return LuaValue();

		// Get return value
		LuaValue retVal;
		switch(lua_type(mState, lua_gettop(mState))){
		case LUA_TBOOLEAN: 
			retVal = LuaValue(lua_toboolean(mState, lua_gettop(mState)));
			lua_pop(mState, 1);
			break;
		case LUA_TNUMBER: 
			retVal = LuaValue(lua_tonumber(mState, lua_gettop(mState)));
			lua_pop(mState, 1);
			break;
		case LUA_TSTRING: 
			retVal = LuaValue(lua_tostring(mState, lua_gettop(mState)));
			lua_pop(mState, 1);
			break;
		default			: break;
		}

		return retVal;
	}

	void Lua::CollectGarbage(){
		//lua_set setgcthreshold(mState, 0);
		lua_gc(mState, LUA_GCCOLLECT, 0);
	}


}
